<!doctype html>
<html lang="zh-cn">
  <head>
    <title>网络编程(2) // JW Blog</title>
    <meta charset="utf-8" />
    <meta name="generator" content="Hugo 0.60.1" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="author" content="John Doe" />
    <meta name="description" content="" />
    <link rel="stylesheet" href="https://duyi111.gitee.io/css/main.min.f90f5edd436ec7b74ad05479a05705770306911f721193e7845948fb07fe1335.css" />

    
    <meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="网络编程(2)"/>
<meta name="twitter:description" content="1.TCP的介绍 1.1 网络应用程序之间的通信过程  通过IP地址能够找到对应的设备，然后再通过端口号找到对应的端口，在通过端口把数据传输给应用程序，这里要注意，数据不能随便发送，在发送之前还需要选择一个对应的传输协议，保证程序之间按照指定的传输规则进行数据的通信。  1.2 TCP的概念  TCP称传输控制协议，它是一种面向连接的、可靠的、基于字节流的传输层通信协议。 TCP通信步骤：  1.创建连接 2.传输数据 3.关闭连接   说明：TCP通信模型相当于生活中的“打电话”，在通信开始之前，一定要先建立好连接，才能发送数据，通信结束要关闭连接。  1.3 TCP的特点  1.面向连接  通信双方必须先建立好连接才能进行数据的传输，数据传输完成后，双方必须断开此连接，以释放系统资源。   2.可靠传输  TCP采用发送应答机制 超时重传 错误校验 流量控制和阻塞管理    1.4 小结  TCP是一个稳定、可靠的传输协议，常用于对数据进行准确的传输，比如：文件下载，浏览器上网。  2.socket的介绍 2.1 socket的概念  socket（简称套接字）是进程之间通信的一个工具，好比现实生活中的插座，所有的家用电器要想工作都是基于插座进行，进程之间想要进行网络通信需要基于这个socket  2.2 socket的作用  负责进程之间的网络数据传输，好比数据的搬运工。  2.3 小结  进程之间网络数据的传输可以通过socket来完成。 socket就是进程间网络数据通信的工具。  3.TCP网络应用程序开发流程 3.1 TCP网络应用程序开发流程的介绍  TCP网络应用程序开发分为：  TCP客户端程序开发 TCP服务端程序开发   说明：  客户端程序是指运行在用户设备上的程序。 服务端程序是指运行在服务器设备上的程序，专门为客户端提供数据服务。    3."/>

    <meta property="og:title" content="网络编程(2)" />
<meta property="og:description" content="1.TCP的介绍 1.1 网络应用程序之间的通信过程  通过IP地址能够找到对应的设备，然后再通过端口号找到对应的端口，在通过端口把数据传输给应用程序，这里要注意，数据不能随便发送，在发送之前还需要选择一个对应的传输协议，保证程序之间按照指定的传输规则进行数据的通信。  1.2 TCP的概念  TCP称传输控制协议，它是一种面向连接的、可靠的、基于字节流的传输层通信协议。 TCP通信步骤：  1.创建连接 2.传输数据 3.关闭连接   说明：TCP通信模型相当于生活中的“打电话”，在通信开始之前，一定要先建立好连接，才能发送数据，通信结束要关闭连接。  1.3 TCP的特点  1.面向连接  通信双方必须先建立好连接才能进行数据的传输，数据传输完成后，双方必须断开此连接，以释放系统资源。   2.可靠传输  TCP采用发送应答机制 超时重传 错误校验 流量控制和阻塞管理    1.4 小结  TCP是一个稳定、可靠的传输协议，常用于对数据进行准确的传输，比如：文件下载，浏览器上网。  2.socket的介绍 2.1 socket的概念  socket（简称套接字）是进程之间通信的一个工具，好比现实生活中的插座，所有的家用电器要想工作都是基于插座进行，进程之间想要进行网络通信需要基于这个socket  2.2 socket的作用  负责进程之间的网络数据传输，好比数据的搬运工。  2.3 小结  进程之间网络数据的传输可以通过socket来完成。 socket就是进程间网络数据通信的工具。  3.TCP网络应用程序开发流程 3.1 TCP网络应用程序开发流程的介绍  TCP网络应用程序开发分为：  TCP客户端程序开发 TCP服务端程序开发   说明：  客户端程序是指运行在用户设备上的程序。 服务端程序是指运行在服务器设备上的程序，专门为客户端提供数据服务。    3." />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://duyi111.gitee.io/post/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B2/" />
<meta property="article:published_time" content="2020-01-30T20:20:04+08:00" />
<meta property="article:modified_time" content="2020-01-30T20:20:04+08:00" />


  </head>
  <body>
    <header class="app-header">
      <a href="https://duyi111.gitee.io/"><img class="app-header-avatar" src="/avatar.jpg" alt="John Doe" /></a>
      <h1>JW Blog</h1>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc vehicula turpis sit amet elit pretium.</p>
      <div class="app-header-social">
        
      </div>
    </header>
    <main class="app-container">
      
  <article class="post">
    <header class="post-header">
      <h1 class ="post-title">网络编程(2)</h1>
      <div class="post-meta">
        <div>
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-calendar">
  <title>calendar</title>
  <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line>
</svg>
          Jan 30, 2020
        </div>
        <div>
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-clock">
  <title>clock</title>
  <circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline>
</svg>
          2 min read
        </div></div>
    </header>
    <div class="post-content">
      <h2 id="1tcp">1.TCP的介绍</h2>
<h4 id="11-">1.1 网络应用程序之间的通信过程</h4>
<ul>
<li>通过IP地址能够找到对应的设备，然后再通过端口号找到对应的端口，在通过端口把数据传输给应用程序，这里要注意，<em><strong>数据不能随便发送，在发送之前还需要选择一个对应的传输协议，保证程序之间按照指定的传输规则进行数据的通信</strong></em>。</li>
</ul>
<h4 id="12-tcp">1.2 TCP的概念</h4>
<ul>
<li>TCP称<em><strong>传输控制协议</strong></em>，它是一种<em><strong>面向连接的、可靠的、基于字节流的传输层通信协议</strong></em>。</li>
<li>TCP通信步骤：
<ul>
<li>1.创建连接</li>
<li>2.传输数据</li>
<li>3.关闭连接</li>
</ul>
</li>
<li>说明：TCP通信模型相当于生活中的“打电话”，在通信开始之前，一定要先建立好连接，才能发送数据，通信结束要关闭连接。</li>
</ul>
<h4 id="13-tcp">1.3 TCP的特点</h4>
<ul>
<li>1.面向连接
<ul>
<li>通信双方必须先建立好连接才能进行数据的传输，数据传输完成后，双方必须断开此连接，以释放系统资源。</li>
</ul>
</li>
<li>2.可靠传输
<ul>
<li>TCP采用发送应答机制</li>
<li>超时重传</li>
<li>错误校验</li>
<li>流量控制和阻塞管理</li>
</ul>
</li>
</ul>
<h4 id="14-">1.4 小结</h4>
<ul>
<li>TCP是一个<em><strong>稳定、可靠的传输协议</strong></em>，常用于<em><strong>对数据进行准确的传输</strong></em>，比如：文件下载，浏览器上网。</li>
</ul>
<h2 id="2socket">2.socket的介绍</h2>
<h4 id="21-socket">2.1 socket的概念</h4>
<ul>
<li>socket（简称<em><strong>套接字</strong></em>）是<em><strong>进程之间通信的一个工具</strong></em>，好比现实生活中的<em><strong>插座</strong></em>，所有的家用电器要想工作都是基于插座进行，<em><strong>进程之间想要进行网络通信需要基于这个socket</strong></em></li>
</ul>
<h4 id="22-socket">2.2 socket的作用</h4>
<ul>
<li>负责<em><strong>进程之间的网络数据传输</strong></em>，好比数据的搬运工。</li>
</ul>
<h4 id="23-">2.3 小结</h4>
<ul>
<li>进程之间<em><strong>网络数据的传输</strong></em>可以通过<em><strong>socket</strong></em>来完成。</li>
<li><em><strong>socket就是进程间网络数据通信的工具</strong></em>。</li>
</ul>
<h2 id="3tcp">3.TCP网络应用程序开发流程</h2>
<h4 id="31-tcp">3.1 TCP网络应用程序开发流程的介绍</h4>
<ul>
<li>TCP网络应用程序开发分为：
<ul>
<li>TCP客户端程序开发</li>
<li>TCP服务端程序开发</li>
</ul>
</li>
<li>说明：
<ul>
<li>客户端程序是指<em><strong>运行在用户设备上的程序</strong></em>。</li>
<li>服务端程序是指<em><strong>运行在服务器设备上的程序</strong></em>，专门为客户端提供数据服务。</li>
</ul>
</li>
</ul>
<h4 id="32-tcp">3.2 TCP客户端程序开发流程的介绍</h4>
<ul>
<li>步骤说明：
<ul>
<li>1.创建客户端套接字对象</li>
<li>2.和服务器套接字建立连接</li>
<li>3.发送数据</li>
<li>4.接收数据</li>
<li>5.关闭客户端套接字</li>
</ul>
</li>
</ul>
<h4 id="33-tcp">3.3 TCP服务器程序开发流程的介绍</h4>
<ul>
<li>步骤说明：
<ul>
<li>1.创建服务端套接字对象</li>
<li>2.绑定端口号</li>
<li>3.设置监听</li>
<li>4.等待接受客户端的连接请求</li>
<li>5.接收数据</li>
<li>6.发送数据</li>
<li>7.关闭套接字</li>
</ul>
</li>
</ul>
<h4 id="34-">3.4 小结</h4>
<ul>
<li>TCP网络应用程序开发分为<em><strong>客户端程序开发</strong></em>和<em><strong>服务端程序开发</strong></em>。</li>
<li>主动发起建立连接请求的是客户端程序。</li>
<li>等待接受连接请求的是服务端程序。</li>
</ul>
<h2 id="4tcp">4.TCP客户端程序开发</h2>
<h4 id="41-socket">4.1 socket类的介绍</h4>
<ul>
<li>导入socket模块
<pre><code>import socket
</code></pre></li>
<li>创建客户端socket对象
<pre><code>socket.socket(AddressFamily, Type)
</code></pre><ul>
<li>参数说明：
<ul>
<li><code>AddressFamily</code>表示IP地址类型，分为IPv4和IPv6</li>
<li><code>Type</code>表示传输协议类型</li>
</ul>
</li>
<li>方法说明：
<ul>
<li>connect((host, port))表示和服务端套接字建立连接，host是服务器IP地址，port是应用程序的端口号。</li>
<li>send(data)表示发送数据，data是二进制数据。
recv(buffersize)表示接收数据，buffersize是每次接收数据的长度。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="42-">4.2 示例代码</h4>
<pre><code>import socket

if __name__ == '__main__':
    # 创建tcp客户端套接字
    # 1. AF_INET：表示IPv4
    # 2.SOCK_STREAM:TCP传输协议
    tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 和服务端应用程序建立连接
    tcp_client_socket.connect(('192.168.131.62', 8080))
    # 代码执行到此，说明连接建立成功
    # 准备发送的数据
    send_data = '你好服务端，我是客户端！'.encode('gbk')
    # 发送数据
    tcp_client_socket.send(send_data)
    # 接收数据，这次接收的数据最大字节数是1024
    recv_data = tcp_client_socket.recv(1024)
    # 返回的直接是服务端程序发送的二进制数据
    print(recv_data)
    # 对数据进行解码
    recv_content = recv_data.decode('gbk')
    print('接收服务端的数据为:', recv_content)
    # 关闭套接字
    tcp_client_socket.close()
</code></pre><ul>
<li>说明：
<ul>
<li>str.encode(编码格式)表示把字符串编码成为二进制。</li>
<li>data.decode(编码格式)表示把二进制解码成为字符串。</li>
</ul>
</li>
</ul>
<h4 id="43-">4.3 小结</h4>
<ul>
<li>1.导入socket模块</li>
<li>2.创建TCP套接字“socket”
<ul>
<li>参数1：<code>AF_INET</code>，表示IPv4地址类型</li>
<li>参数2：<code>SOCK_STREAM</code>，表示TCP传输协议类型</li>
</ul>
</li>
<li>3.发送数据：<code>send</code>
<ul>
<li>参数1；要发送的二进制数据，注意：字符串需要使用encode()方法进行编码</li>
</ul>
</li>
<li>4.接收数据：<code>recv</code>
<ul>
<li>参数1：表示每次接收数据的大小，单位是字节</li>
</ul>
</li>
<li>5.关闭套接字<code>socket</code>表示通信完成。</li>
</ul>
<h2 id="5tcp">5.TCP服务端程序开发</h2>
<h4 id="51-socket">5.1 socket类的介绍</h4>
<ul>
<li>导入socket模块
<pre><code>import socket
</code></pre></li>
<li>创建服务端socket对象
<pre><code>socket.socket(AddressFamliy, Type)
</code></pre></li>
<li>参数说明：
<ul>
<li><code>AddressFamily</code>表示IP地址类型，分为<code>IPv4</code>和<code>IPv6</code></li>
<li><code>Type</code>表示传输协议类型</li>
</ul>
</li>
<li>方法说明：
<ul>
<li><code>bind((host, port))</code>表示绑定端口号，<code>host</code>是IP地址，<code>port</code>是端口号，IP地址一般不指定，表示本机的任何一个IP地址都可以。</li>
<li><code>listen(backlog)</code>表示设置监听，<code>backlog</code>参数表示最大等待建立连接的个数。</li>
<li><code>accept()</code>表示等待接受客户端的连接请求。</li>
<li><code>send(data)</code>表示发送数据，<code>data</code>是二进制数据。</li>
<li><code>recv(buffersize)</code>表示接收数据，<code>buffersize</code>是每次接收数据的长度。</li>
</ul>
</li>
</ul>
<h4 id="52-">5.2 示例代码</h4>
<pre><code>import socket


if __name__ == '__main__':
    # 创建TCP服务端套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置端口号复用，让程序退出端口号立即释放
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 给程序绑定端口号
    tcp_server_socket.bind((&quot;&quot;, 8989))
    # 设置监听
    # 128：最大等待建立连接的个数，提示：目前是单任务的服务端，同一时刻只能服务与一个客户端，后续使用多任务能够让服务端同时服务于多个客户端，不需要让客户端进行等待建立连接
    # listen后的这个套接字值负责接收客户端连接请求，不能收发信息，收发信息使用返回的这个新套接字来完成
    tcp_server_socket.listen(128)
    # 等待客户端建立连接的请求，只有客户端和服务端建立连接成功代码才会解阻塞，代码才能继续往下执行
    # 1.专门和客户端通信的套接字：server_client_socket
    # 2.客户端的IP地址和端口号：ip_port
    server_client_socket, ip_port = tcp_server_socket.accept()
    # 代码执行到此说明连接建立成功
    print('客户端的ip地址和端口号:', ip_port)
    # 接收客户端发送的数据，这次接收数据的最大字节数是1024
    recv_data = service_client_socket.recv(1024)
    # 获取数据的长度
    recv_content = recv_data.decode('gdk')
    print('接收客户端的数据为:', recv_content)
    # 准备发送的数据
    send_data = 'ok，问题正在处理中。。。'.encode('gdk')
    # 发送数据给客户端
    service_client_socket.send(send_data)
    # 关闭服务与客户端的套接字，终止和客户端通信的服务
    service_client_socket.close()
    # 关闭服务端的套接字，终止和客户端提供建立连接请求的服务
    tcp_server_socket.close()
</code></pre><ul>
<li>说明：
<ul>
<li>当客户端和服务端建立连接后，服务端程序退出后端口号不会立即释放，需要等待大概1-2分钟。</li>
<li>解决办法有两种：
<ul>
<li>1.更换服务端端口号</li>
<li>2.设置端口复用（推荐），也就是说让服务端程序退出后端口号立即释放。</li>
</ul>
</li>
<li>设置端口号复用的代码：
<pre><code># 参数1：表示当前套接字
# 参数2：设置端口号复用选项
# 参数3：设置端口号复用选项对应的值
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
</code></pre></li>
</ul>
</li>
</ul>
<h4 id="53-">5.3 小结</h4>
<ul>
<li>
<p>1.导入<code>socket</code>模块</p>
</li>
<li>
<p>2.创建TCP套接字<code>socket</code></p>
<ul>
<li>参数1：<code>AF_INET</code>：表示<code>IPv4</code>地址类型</li>
<li>参数2：<code>SOCK_STREAM</code>：表示TCP传输协议类型</li>
</ul>
</li>
<li>
<p>3.绑定端口号<code>bind</code></p>
<ul>
<li>参数：元组，比如（IP地址, 端口号）</li>
</ul>
</li>
<li>
<p>4.设置监听<code>listen</code></p>
<ul>
<li>参数：最大等待建立连接的个数</li>
</ul>
</li>
<li>
<p>5.等待接受客户端的连接请求<code>accept</code></p>
</li>
<li>
<p>6.发送数据<code>send</code></p>
<ul>
<li>参数：要发送的二进制数据，注意：字符串需要使用encode()方法进行编码</li>
</ul>
</li>
<li>
<p>7.接收数据<code>recv</code></p>
<ul>
<li>参数：表示每次接收数据的大小，单位是字节，注意：解码成字符串使用<code>decode()</code>方法</li>
</ul>
</li>
<li>
<p>8.关闭套接字<code>socket</code>表示通信完成</p>
</li>
</ul>
<h2 id="6tcp">6.TCP网络应用程序的注意点</h2>
<ul>
<li>1.当TCP客户端程序想要和TCP服务端程序进行通信的时候必须要先<em><strong>建立连接</strong></em>。</li>
<li>2.TCP客户端程序一般不需要绑定端口号，因为客户端是主动发起建立连接的。</li>
<li>3.<em><strong>TCP服务端程序必须绑定端口号</strong></em>，否则客户端找不到这个TCP服务度程序。</li>
<li>4.listen后的套接字是被动套接字，<em><strong>只负责接收新的客户端的连接请求，不能收发消息</strong></em>。</li>
<li>5.当TCP客户端程序和TCP服务端程序连接成功后，TCP服务端程序会产生一个<em><strong>新的套接字</strong></em>，收发客户端消息使用该套接字。</li>
<li>6.<em><strong>关闭accept返回的套接字意味着和这个客户端已经通信完毕</strong></em>。</li>
<li>7.<em><strong>关闭listen后的套接字意味着服务端的套接字关闭了，会导致新的客户端不能连接服务器，但是之前已经接成功的客户端还能正常通信</strong></em>。</li>
<li>8.<em><strong>当客户端的套接字调用close后，服务器端的recv会解阻塞，返回的数据长度为0</strong></em>，服务端可以通过返回数据的长度来判断客户端是否已经下线，反之<em><strong>服务端关闭套接字，客户端的recv也会解阻塞，返回的数据长度也为0</strong></em>。</li>
</ul>
<h2 id="7socketsendrecv">7.socket之send和recv原理剖析</h2>
<h4 id="71-tcpsocket">7.1 认识TCP中socket的发送和接收缓冲区</h4>
<ul>
<li>当创建一个TCP socket对象的时候会有一个<em><strong>发送缓冲区</strong></em>和一个<em><strong>接收缓冲区</strong></em>，<em><strong>这个发送和接收缓冲区指的是内存中的一片空间</strong></em>。</li>
</ul>
<h4 id="72-send">7.2 send原理剖析</h4>
<ul>
<li>send不是直接把数据发送给服务端</li>
<li>要想发数据，必须得<em><strong>通过网卡发送数据</strong></em>，应用程序是无法直接通过网卡发送数据的，它需要调用操作系统接口，也就是说，应用程序把发送的数据先写入到<em><strong>发送缓冲区</strong></em>（内存中的一片空间），再<em><strong>由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡</strong></em>。</li>
</ul>
<h4 id="73-recv">7.3 recv原理剖析</h4>
<ul>
<li>recv不是直接从客户端接收数据</li>
<li><em><strong>应用软件是无法直接通过网卡接收数据的</strong></em>，它需要调用操作系统接口，<em><strong>由操作系统通过网卡接收数据</strong></em>*，把接收的数据写入到接收缓冲区（内存中的一片空间），应用程序***再从接收缓冲区获取客户端发送的数据***。</li>
</ul>
<h4 id="74-">7.4 小结</h4>
<ul>
<li>发送数据是发送到<em><strong>发送缓冲区</strong></em></li>
<li>接收数据是从<em><strong>接收缓冲区</strong></em>获取</li>
<li>不管是recv还是send都不是直接接收到对方的数据和发送数据到对方，<em><strong>发送数据会写入到发送缓冲区，接收数据是从接收缓冲区来读取，发送数据和接收数据最终是由操作系统控制网卡来完成</strong></em>。</li>
</ul>

    </div>
    <div class="post-footer">
      
    </div>
  </article>

    </main>
  </body>
</html>
